#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sqlite3.h>
#include <random>
#include <sstream>
#include <iomanip>
#include <atomic>

#include <sys/stat.h>
#include <cstdlib>
#include <cerrno>

#include "mq_log.hpp"
namespace ns_kkymq
{
    using namespace ns_log;
    class SqliteHelper
    {
    public:
        typedef int (*Sqlite3Callback)(void *, int, char **, char **);
        SqliteHelper(const std::string &dbfile)
            : _dbfile(dbfile), _handler(nullptr)
        {
        }

        bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
        {
            // int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
            // 读写方式打开 没有就创建  safe_level启用 SQLite 的完全互斥模式
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler,
                                      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level, nullptr);
            if (ret != SQLITE_OK)
            {
                lg(Fatal, "创建/打开sqlite数据库失败!: %s", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }

        bool exec(const std::string &sql, Sqlite3Callback cb, void *arg)
        {
            // int sqlite3_exec(sqlite3*, char *sql, int (*callback)(void*,int,char**,char**), void* arg, char **err);
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                lg(Error, "%s \n执行sqlite3_exec失败!: %s", sql.c_str(), sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }

        void close()
        {
            // int sqlite3_close_v2(sqlite3*);
            if (_handler)
                sqlite3_close_v2(_handler);
        }

    private:
        std::string _dbfile;
        sqlite3 *_handler;
    };

    class StrHelper
    {
    public:
        // 字符串切割函数
        // animal...dog..leg. sep = '.'
        // 切割到vector里
        static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
        {
            int pos = 0, index = 0;
            while (index < str.size())
            {
                pos = str.find(sep, index);
                if (pos == std::string::npos)
                {
                    result.emplace_back(str.substr(index));
                    return result.size();
                }
                if (pos != index)
                {
                    result.emplace_back(str.substr(index, pos - index));
                    index = pos + sep.size();
                    continue;
                }
                index += sep.size();
            }
            return result.size();
        }
    };

    class UUIDHelper
    {
    public:
        static std::string uuid()
        {
            std::random_device rd;
            // rd()为机器随机数，效率低
            // 通过rd()作为生成伪随机数的种子
            std::mt19937_64 gernator(rd()); // 梅森旋转算法，生成为随机数

            // 限制数字范围0-255
            std::uniform_int_distribution<int> distribution(0, 255);

            // 生成的数字转化为16进制数字字符（注意要填充为2个字符）
            std::stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(gernator);
                if (i == 3 || i == 5 || i == 7)
                {
                    ss << "-";
                }
            }
            // 原子性整数
            static std::atomic<size_t> seq(1);
            // 原子性的 sep+=1
            size_t num = seq.fetch_add(1);
            for (int i = 7; i >= 0; i--)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i * 8)) & 0xff);
                if (i == 6)
                    ss << '-';
            }
            return ss.str();
        }
    };

    class FileHelper
    {
    public:
        FileHelper(const std::string &filename)
            : _filename(filename)
        {
        }
        bool exists()
        {
            struct stat st;
            return stat(_filename.c_str(), &st) == 0;
        }
        size_t size()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
            {
                return 0;
            }
            return st.st_size;
        }

        bool read(std::string &body)
        {
            // 获取文件大小，根据文件大小调整body空间
            size_t fsize = this->size();
            body.resize(fsize);
            return read(&body[0], 0, fsize);
        }

        // body为读取到这里 offset为偏移量，len为读取多少长度
        bool read(char *body, size_t offset, size_t len)
        {
            // 1.打开文件
            std::ifstream ifs(_filename, std::ios::binary | std::ios::in);
            if (ifs.is_open() == false)
            {
                lg(Error, "%s 文件打开失败!", _filename.c_str());
                return false;
            }
            // 2.跳转文件读写位置  从begin跳转到offset这个位置
            ifs.seekg(offset, std::ios::beg);
            // 3.读取数据数据
            ifs.read(body, len);
            if (ifs.good() == false)
            {
                lg(Error, "%s 文件读取失败!", _filename.c_str());
                ifs.close();
                return false;
            }
            // 4.关闭文件
            ifs.close();
            return true;
        }

        bool write(const std::string &body)
        {
            return write(body.c_str(), 0, body.size());
        }

        bool write(const char *body, size_t offset, size_t len)
        {
            // 1.打开文件
            std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out);
            if (fs.is_open() == false)
            {
                lg(Error, "%s 文件打开失败!", _filename.c_str());
                return false;
            }
            // 2.跳转文件读写位置  从begin跳转到offset这个位置
            fs.seekg(offset, std::ios::beg);
            // 3.写入数据
            fs.write(body, len);
            if (fs.good() == false)
            {
                lg(Error, "%s 文件写入失败!", _filename.c_str());
                fs.close();
                return false;
            }
            // 4.关闭文件
            fs.close();
            return true;
        }

        static std::string parentDirectory(const std::string &filename)
        {
            size_t pos = filename.find_last_of("/");
            if (pos == std::string::npos)
            {
                return ".";
            }
            std::string path = filename.substr(0, pos);
            return path;
        }

        bool rename(const std::string newname)
        {
            return (::rename(_filename.c_str(), newname.c_str()) == 0);
        }

        static bool createFile(const std::string &filename)
        {
            // 1.打开文件
            std::ofstream ofs(filename, std::ios::binary | std::ios::out);
            if (ofs.is_open() == false)
            {
                lg(Error, "%s 文件打开失败!", filename.c_str());
                return false;
            }
            ofs.close();
            return true;
        }
        static bool removeFile(const std::string &filename)
        {
            return (::remove(filename.c_str()) == 0);
        }
        static bool createDirectory(const std::string &path)
        {
            // 在多级路径创建中，我们需要从第一个父级目录开始创建
            size_t pos, index = 0;
            while (index < path.size())
            {
                pos = path.find('/', index);
                if (pos == std::string::npos)
                {
                    return mkdir(path.c_str(), 0775) == 0;
                }
                std::string subpath = path.substr(0, pos);
                int ret = mkdir(subpath.c_str(), 0775);
                if (ret != 0 && errno != EEXIST)
                {
                    lg(Error, "创建目录 %s 失败", subpath.c_str());
                    return false;
                }
                index = pos + 1;
            }
            return true;
        }
        static bool removeDirectory(const std::string &path)
        {
            std::string cmd = "rm -rf " + path;
            return system(cmd.c_str()) != -1;
        }

    private:
        std::string _filename;
    };
}